package com.megvii.corelib.update;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.megvii.corelib.update.task.UpdateErrorCode;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by linyue on 16/3/4.
 */
public class UpdateManager
{
    public static final String LOG_TAG = "UpdateManager";

    public static final int MAX_PROGRESS = 100;

    private ExecutorService threadPool = null;

    private ArrayList<UpdateManagerListener> listeners = new ArrayList<>();

    private Handler handler;

    private DownloadThread downloadThread;

    private boolean downloading = false;

    private boolean pauseDownload = false;

    private volatile boolean resetingDownload = false;

    private String downloadingUrl;

    private String downloadingAPKName;

    private int currentProgress = 0;

    public void init()
    {
        threadPool = Executors.newCachedThreadPool();
        handler = new Handler(Looper.getMainLooper());
    }

    public void release()
    {
        synchronized (listeners)
        {
            listeners.clear();
        }
        threadPool.shutdownNow();
        threadPool = null;
        stopDownload();
    }

    //    public void prepareStartDownload(String urlString, final String fileName)
    //    {
    //        int rangeStart = UpdateUtil.getFileSize(fileName);
    //        CheckSizeTask task = new CheckSizeTask(urlString, rangeStart);
    //        task.setListener(new CheckSizeTask.CheckSizeListener()
    //        {
    //            @Override
    //            public void onCheckSizeResult(final CheckSizeTask task, final int size)
    //            {
    //                handler.post(new Runnable()
    //                {
    //                    @Override
    //                    public void run()
    //                    {
    //                        synchronized (listeners)
    //                        {
    //                            for (UpdateManagerListener listener : listeners)
    //                            {
    //                                listener.onGetSpaceNeeded(task.urlString, fileName, size);
    //                            }
    //                        }
    //                    }
    //                });
    //            }
    //        });
    //        threadPool.submit(task);
    //    }

    public void downloadAPK(String url, String fileName)
    {
        synchronized (this)
        {
            if (!isDownloading())
            {
                this.downloadingUrl = url;
                this.downloadingAPKName = fileName;
                downloading = true;
                pauseDownload = false;
                createAndStartDownloadThread(url, fileName);
            }
        }
    }

    public void resetDownload(final String url, final String fileName)
    {
        if (resetingDownload)
        {
            return;
        }
        synchronized (this)
        {
            resetingDownload = true;
            stopDownload();
            UpdateUtil.deleteFile(fileName);
            downloadAPK(url, fileName);
            resetingDownload = false;
        }
    }

    public void stopDownload()
    {
        synchronized (this)
        {
            if (isDownloading())
            {
                downloadThread.interrupt();
                try
                {
                    downloadThread.join();
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                downloadThread = null;
                downloading = false;
            }
        }
    }

    public void pauseDownload()
    {
        synchronized (this)
        {
            Log.d(LOG_TAG, "pauseDownload ");
            pauseDownload = true;
        }
    }

    public void resumeDownload()
    {
        synchronized (this)
        {
            if (downloadingUrl == null || downloadingAPKName == null)
            {
                return;
            }
            if (isDownloading())
            {
                pauseDownload = false;
                synchronized (downloadThread)
                {
                    try
                    {
                        downloadThread.notifyAll();
                    }
                    catch (IllegalMonitorStateException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
            else
            {
                downloadAPK(downloadingUrl, downloadingAPKName);
            }
        }
    }

    public boolean isPaused()
    {
        synchronized (this)
        {
            return pauseDownload;
        }
    }

    private void createAndStartDownloadThread(String url, String fileName)
    {
        downloadThread = new DownloadThread(url, fileName);
        downloadThread.start();
    }

    public boolean isDownloading()
    {
        synchronized (this)
        {
            if (downloading)
            {
                if (!downloadThread.isAlive())
                {
                    downloading = false;
                    downloadThread = null;
                }
            }
            return downloading;
        }
    }

    public String getDownloadingUrl()
    {
        return downloadingUrl;
    }

    public String getDownloadingAPKName()
    {
        return downloadingAPKName;
    }

    public int getCurrentProgress()
    {
        return currentProgress;
    }

    private int calculateProgress(int downloadedSize, int totalSize)
    {
        int progress = (int) ((long) downloadedSize * (long) MAX_PROGRESS / (long) totalSize);
        return progress;
    }

    public void registerListener(UpdateManagerListener listener)
    {
        synchronized (listeners)
        {
            listeners.add(listener);
        }
    }

    public void unregisterListener(UpdateManagerListener listener)
    {
        synchronized (listeners)
        {
            listeners.remove(listener);
        }
    }

    private void postDownloadStart()
    {
        handler.post(new Runnable()
        {
            @Override
            public void run()
            {
                synchronized (listeners)
                {
                    for (UpdateManagerListener listener : listeners)
                    {
                        listener.onDownloadStart();
                    }
                }
            }
        });
    }

    private void postDownloadProgress(final int progress)
    {
        handler.post(new Runnable()
        {
            @Override
            public void run()
            {
                synchronized (listeners)
                {
                    for (UpdateManagerListener listener : listeners)
                    {
                        listener.onDownloadProgress(progress);
                    }
                }
            }
        });
    }

    private void postDownloadCompleted(final String fileName)
    {
        handler.post(new Runnable()
        {
            @Override
            public void run()
            {
                synchronized (listeners)
                {
                    for (UpdateManagerListener listener : listeners)
                    {
                        listener.onDownloadCompleted(fileName);
                    }
                }
            }
        });
    }

    private void postDownloadError(final String url, final String fileName, final int error)
    {
        handler.post(new Runnable()
        {
            @Override
            public void run()
            {
                synchronized (listeners)
                {
                    for (UpdateManagerListener listener : listeners)
                    {
                        listener.onDownloadError(url, fileName, error);
                    }
                }
            }
        });
    }

    private void postDownloadInterrupt()
    {
        handler.post(new Runnable()
        {
            @Override
            public void run()
            {
                synchronized (listeners)
                {
                    for (UpdateManagerListener listener : listeners)
                    {
                        listener.onDownloadInterrupt();
                    }
                }
            }
        });
    }

    public interface UpdateManagerListener
    {
        public void onGetSpaceNeeded(String urlString, String fileName, int size);

        public void onDownloadStart();

        /**
         * 0 - {@code MAX_PROGRESS}
         *
         * @param progress
         */
        public void onDownloadProgress(int progress);

        public void onDownloadCompleted(String fileName);

        public void onDownloadError(String url, String fileName, int errorCode);

        public void onDownloadInterrupt();
    }

    public class DownloadThread extends Thread
    {
        public String url;

        public String fileName;

        private volatile boolean terminate = false;

        public DownloadThread(String url, String fileName)
        {
            super();
            this.url = url;
            this.fileName = fileName;
        }

        @Override
        public void run()
        {
            FileOutputStream fos = null;
            HttpURLConnection connection = null;
            try
            {
                //先检查总长度
                connection = UpdateUtil.createConnection(url);
                final int totalSize = connection.getContentLength();
                connection.disconnect();
                if (!isInterrupted() && !terminate)
                {
                    postDownloadStart();
                    //
                    int rangeStart = UpdateUtil.getFileSize(fileName);
                    if (rangeStart == totalSize)
                    {
                        postDownloadProgress(MAX_PROGRESS);
                        postDownloadCompleted(fileName);
                    }
                    else
                    {
                        if (rangeStart > totalSize)
                        {
                            //fuck to enter this block...
                            File file = UpdateUtil.getFile(fileName);
                            file.delete();
                        }
                        rangeStart = UpdateUtil.getFileSize(fileName);
                        connection = UpdateUtil.createConnection(url, rangeStart);
                        int downloadedSize = rangeStart;
                        if (!UpdateUtil.isSpaceEnough(downloadedSize))
                        {
                            postDownloadError(url, fileName, UpdateErrorCode.SPACE_NOT_ENOUGH);
                            connection.disconnect();
                            return;
                        }
                        File file = UpdateUtil.getFile(fileName);
                        fos = new FileOutputStream(file, true);
                        final int bufferSize = 1024;
                        byte[] buffer = new byte[bufferSize];
                        InputStream is = connection.getInputStream();
                        int readSize = is.read(buffer);
                        while (readSize != -1 && !isInterrupted() && !terminate)
                        {
                            try
                            {
                                fos.write(buffer, 0, readSize);
                                fos.flush();
                            }
                            catch (IOException e)
                            {
                                e.printStackTrace();
                                postDownloadError(url, fileName, UpdateErrorCode.SPACE_NOT_ENOUGH);
                                fos.close();
                                connection.disconnect();
                                return;
                            }
                            downloadedSize += readSize;
                            currentProgress = calculateProgress(downloadedSize, totalSize);
                            postDownloadProgress(currentProgress);
                            //判断是否暂停
                            if (pauseDownload)
                            {
                                synchronized (this)
                                {
                                    try
                                    {
                                        this.wait();
                                    }
                                    catch (InterruptedException e)
                                    {
                                        e.printStackTrace();
                                        postDownloadInterrupt();
                                        fos.close();
                                        connection.disconnect();
                                        return;
                                    }
                                }
                            }
                            readSize = is.read(buffer);
                        }
                    }
                    if (!isInterrupted() && !terminate)
                    {
                        postDownloadCompleted(fileName);
                    }
                }
            }
            catch (FileNotFoundException e)
            {
                e.printStackTrace();
                postDownloadError(url, fileName, UpdateErrorCode.NO_PERMISSION_TO_WRITE_FILE);
            }
            catch (IOException e)
            {
                e.printStackTrace();
                if (!resetingDownload)
                {
                    postDownloadError(url, fileName, UpdateErrorCode.NETWORK_DISCONNECT);
                }
            }
            if (fos != null)
            {
                try
                {
                    fos.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
            if (connection != null)
            {
                connection.disconnect();
            }
        }

        @Override
        public void interrupt()
        {
            terminate = true;
            super.interrupt();
        }
    }
}
